import population


class Species(population.Population):
    def __new__(cls):
        return population.Population.__new__(cls)
    def __init__(self):
#        population.Population.__init__(self)
        self.average = 0
        self.by_signature = {}

    def select(self):
        """Selects an element from a selected species to given specimen."""
        return self[population.generateIndex(len(self))].select()


    def evaluate(self, pop):
        return pop.average * pop.tryAvg * pop.entry_size


    def add(self, entry, value):
        """Newly adds and instates a previously not contained entry to one of the species."""

        if not self.by_signature.has_key(entry.layers):
            pop = self.addPop(entry.layers, len(entry))
        else:
            pop = self.by_signature[entry.layers]

        n = len(self)
        self.average -= self.evaluate(pop)/n
        pop.add(entry, value)
        self.average += self.evaluate(pop)/n
        self.update(pop)

    def addPop(self, layers, size):
        pop = self.setup_pop(layers)
        pop.layers = layers
        pop.entry_size = size
        self.by_signature[layers] = pop
        self.append(pop)
        self.update(pop)
        n = len(self)
        self.average *= (n-1)/n
        return pop


    def valuate(self, entry, *args):
        """Assigns a value to an already added entry."""

        pop = self.by_signature[entry.layers]
        n = len(self)
        self.average -= self.evaluate(pop)/n
        pop.valuate(entry, *args)
        self.average += self.evaluate(pop)/n
        self.update(pop)


    def update(self, pop):
        """Moves a pop to its correct position in self.

           Assumes that all other entries are in correct order. Not threadsafe!"""

        if self.evaluate(pop) > population.CUTOFF_FACTOR * self.average\
         and len(self) > 5:
            self.make_extinct(pop)
        else:
            index = self.index(pop)
            n = len(self)
            i = index - 1
            while i and self.evaluate(pop) < self.evaluate(self[i]):
                i -= 1

            i += 1

            if i == index:
                i += 1
                while i < n and self.evaluate(pop) > self.evaluate(self[i]):
                    i += 1

                i -= 1

            self.remove(pop)
            self.insert(i, pop)

    def _kill(self, entry):
        pop = self.by_signature[entry.layers]
        if len(pop) > pop.min_size:
            n = len(self)
            self.average -= self.evaluate(pop)/n
            pop._kill(entry)
            self.average += self.evaluate(pop)/n
            self.update(pop)
        else:
            self.killPop(pop)

    def killPop(self, pop):
        self.remove(pop)
        del self[pop.layers]
        n = len(self)
        self.average *= (n+1)/n
        self.average -= self.evaluate(pop)/n
